package paper.cluster;

import java.sql.*;

import java.io.PrintWriter;
import java.io.FileWriter;
import java.io.IOException;

import java.util.HashSet;
import java.util.Date;
import java.util.Random;
import java.util.Iterator;
import java.util.Vector;

import paper.db.DB;
import paper.db.Paper;
import paper.db.PaperCache;

class EdgeTree
{
    Paper root, target;
    HashSet<Paper> parents;
    EdgeTree[] children;
    int depth;

    EdgeTree(Paper p, Paper t)
    {
        root = p;
        target = t;
        parents = new HashSet<Paper>();
        parents.add(root);
        depth = 0;
    }

    EdgeTree(Paper p, EdgeTree et)
    {
        root = p;
        target = et.target;
        parents = new HashSet<Paper>(et.parents);
        parents.add( root );
        depth = et.depth + 1;
    }
    
    HashSet<Paper> iterativeDeepen(int bound)
    {
        for( int i=1; i<=bound; i++)
        {
            HashSet<Paper> result = addLevel(i);
            if( result!= null )
                return result;
        }
        return null;
    }

    HashSet<Paper> addLevel(int n)
    {
        HashSet<Paper> result;
        if(n<=0)
            return null;
        if( children==null )
        {
            children = new EdgeTree[ root.cocites.size() ];
            int cnt=0;
            for(Iterator<Paper> i=root.cocites.iterator(); 
                i.hasNext(); )
            {
                Paper np = i.next();
                if( !parents.contains(np) )
                {
                    if( depth>0 && np==target )
                    {
                        result = new HashSet<Paper>(parents);
                        result.add(np);
                        return result;
                    }
                    children[cnt++] = new EdgeTree( np, this);
                }
            }
        }
        for(int i=0; i<children.length; i++)
        {
            if( children[i]!=null )
            {
                result = children[i].addLevel(n-1);
                if( result!=null )
                    return result;  
            }
        }
        return null;
    }
}

public class Cluster 
{
    public void localGraph(Paper p, int n)
    {
        if( n<0 || p.mark>0)
            return;
        p.mark = 1;
        p.getCocitations();
        System.out.println("localGraph("+n+"): " + p);
        for(Iterator<Paper> i=p.cocites.iterator(); i.hasNext(); )
        {
            localGraph( i.next(), n-1) ;
        }
    }

    public Cluster() throws IOException
    {
        PaperCache cache = new PaperCache();
        Paper start = Paper.loadOnKey("1dfc3abf8adffd53", cache);

        cache.clearMarks();
        localGraph(start,1);
        HashSet<Paper> subgraph = cache.getMarked(1);

        PrintWriter out = new PrintWriter(new FileWriter( "cocite.dot" ));
        out.println("digraph{");
        dot( out, subgraph, start, null );
        out.println("}");

        for(Iterator<Paper> i=start.cocites.iterator(); i.hasNext(); )
        {
            Paper first = i.next();
            EdgeTree e = new EdgeTree(first,start);
            HashSet<Paper> cycle = e.iterativeDeepen(10);
            out.println("digraph{");
            for(Iterator<Paper> j=subgraph.iterator(); j.hasNext(); )
                j.next().mark=1;
            dotCycle( out, subgraph, start, null, cycle );
            out.println("}");
        }
        out.close();
    }

    public void dotCycle(PrintWriter out, HashSet<Paper> set, Paper p, 
                         Paper cut, HashSet<Paper> cycle)
    {
        if( p.mark!=1 )
            return;
        p.mark=0;
        if( p!=cut )
        {
            if( cycle.contains(p) )
                out.println("x"+p.key + " [color=red,label=\"" + p.title.replace('\n',' ').replace('"','!') + "\"];");
            else
                out.println("x"+p.key + " [label=\"" + p.title.replace('\n',' ').replace('"','!') + "\"];");
        }
        for(Iterator<Paper> i=p.cocites.iterator(); i.hasNext(); )
        {
            Paper t = i.next();
            if( t != null )         // Why the fuck is t null sometimes?
            {
                if( set.contains(t) && 
                    String.CASE_INSENSITIVE_ORDER.compare(p.key,t.key)<0 
                    && t!=cut && p!=cut) 
                {
                    if( cycle.contains(p) && cycle.contains(t) )
                        out.println("x"+p.key + " -> " + "x"+t.key + " [dir=none,color=red];");
                    else
                        out.println("x"+p.key + " -> " + "x"+t.key + " [dir=none];");
                }
                if( t.mark == 1 )
                    dotCycle( out, set, t, cut, cycle);
            }
        }
    }

    public void dot(PrintWriter out, HashSet<Paper> set, Paper p, Paper cut)
    {
        if( p.mark!=1 )
            return;
        p.mark=0;
        if( p!=cut )
            out.println("x"+p.key + " [label=\"" + p.title.replace('\n',' ').replace('"','!') + "\"];");
        for(Iterator<Paper> i=p.cocites.iterator(); i.hasNext(); )
        {
            Paper t = i.next();
            if( t != null )         // Why the fuck is t null sometimes?
            {
                if( set.contains(t) && 
                    String.CASE_INSENSITIVE_ORDER.compare(p.key,t.key)<0 
                    && t!=cut && p!=cut) 
                    out.println("x"+p.key + " -> " + "x"+t.key + " [dir=none];");
                if( t.mark == 1 )
                    dot( out, set, t, cut);
            }
        }
    }

    public void buildCociteGraph(Paper p)
    {
        for(Iterator<Paper> i=p.cocites.iterator(); i.hasNext(); )
        {
            Paper nxt = i.next();
            if( nxt.mark == 0)
            {
                nxt.getCocitations();
                nxt.mark = -1;
            }
        }
        for(Iterator<Paper> i=p.cocites.iterator(); i.hasNext(); )
        {
            Paper nxt = i.next();
            if( nxt.mark == -1)
            {
                nxt.mark = p.mark+1;
                buildCociteGraph( nxt );
            }
        }
    }


    public static void main(String[] args) throws IOException
    {
        new Cluster();
    }

}
